/**
 * Author(s): xuming.Wong <xuming.Wong@gmail.com>
 */
#include "TerrainManager.h"

#include "OgrePageManager.h"//paging
#include "OgreTerrain.h"//terrain
#include "OgreTerrainGroup.h"
#include "OgreTerrainPaging.h"

#include "TerrainDecal.h"
#include "SceneObject.h"

namespace PQEngine
{
	const float TerrainManager::TERRAIN_DEFAULT_RESTITUTION=0.1f;
	const float TerrainManager::TERRAIN_DEFAULT_FRICTION= 0.8f;
	const std::string TerrainManager::TERRAIN_FILE_PREFIX="terrain";
	const std::string TerrainManager::TERRAIN_FILE_SUFFIX="terrain";
	

	TerrainManager::TerrainManager(SceneObject *sceneObject)
		: _sceneObject(sceneObject),_isEditing(false),_terrainGroupObject(0),_selectedTerrain(0),
		_brushSize(0.02f),_position(Ogre::Vector3(0,0,0))
	{
		assert(_sceneObject);
		_sceneMgr=_sceneObject->getSceneManager();

		initTerrainDecal();
		initTerrainGlobalOptions();
		initTerrainGroup();
		
	}

	void TerrainManager::initTerrainDecal()
	{
		_decalFrustum=new Ogre::Frustum();
		_decalFrustum->setProjectionType(Ogre::PT_ORTHOGRAPHIC);
		_decalFrustum->setFOVy(Ogre::Degree(45));
		_decalFrustum->setNearClipDistance(10);
		_decalFrustum->setOrthoWindow(10.0f,10.0f);
		_decalFrustum->setVisible(false);

		_decalNode=_sceneMgr->getRootSceneNode()->createChildSceneNode("TerrainDecalProjectorNode");
		_decalNode->attachObject(_decalFrustum);
		_decalNode->setPosition(0.0f,5.0f,0.0f);
		_decalNode->setOrientation(Ogre::Quaternion(Ogre::Degree(-90.0f), Ogre::Vector3::UNIT_X));
		
	}

	TerrainManager::~TerrainManager()
	{
		if(_decalFrustum){
			_decalFrustum->detachFromParent();
			delete _decalFrustum;
			_decalFrustum=NULL;
		}

		if(_decalNode){
			_decalNode->getParentSceneNode()->removeAndDestroyChild(_decalNode->getName());
			_decalNode=NULL;
		}

	}

	void TerrainManager::addTerrainLayer(std::string textureName)
	{
		if(_selectedTerrain){
			_selectedTerrain->addLayer(textureName);
		}
	}

	void TerrainManager::addTerrainLayer(TerrainObject* terrainObject,std::string textureName)
	{
		if(terrainObject){
			terrainObject->addLayer(textureName);
		}
	}


	void TerrainManager::addTerrainLayer(int textureIndex)
	{
		if(_selectedTerrain){
			_selectedTerrain->addLayer(textureIndex);
		}
	}

	void TerrainManager::addTerrainLayer(TerrainObject* terrainObject,int textureIndex)
	{
		if(terrainObject){
			terrainObject->addLayer(textureIndex);
		}
	}



	StringVector TerrainManager::getTextureLayerName()
	{
		return _selectedTerrain->getLayerNameList();
	}

	TerrainGroupObject* TerrainManager::getTerrainGroup()
	{
		
		return _terrainGroupObject;
	}

	void TerrainManager::initTerrainGroup()
	{
		_terrainGroup = OGRE_NEW Ogre::TerrainGroup(_sceneMgr, Ogre::Terrain::ALIGN_X_Z, TERRAIN_SIZE, TERRAIN_WORLD_SIZE);
		_terrainGroup->setFilenameConvention(TERRAIN_FILE_PREFIX, TERRAIN_FILE_SUFFIX);
		_terrainGroup->setOrigin(_position);
		_terrainGroupObject=new TerrainGroupObject(_terrainGroup);//data representation of OGRE::TerrainGroup

		/*
		Ogre::Terrain::ImportData& import=_terrainGroup->getDefaultImportSettings();
		
		import.terrainSize=TERRAIN_SIZE;
		import.worldSize=TERRAIN_WORLD_SIZE;

		import.layerList.resize(1);
		import.layerList[0].worldSize=30;
		import.layerList[0].textureNames.push_back("ice_3_diffuse.png");
		

		// Configure default import settings for if we use imported image
		Terrain::ImportData& defaultimp = mTerrainGroup->getDefaultImportSettings();
		defaultimp.terrainSize = TERRAIN_SIZE;
		defaultimp.worldSize = TERRAIN_WORLD_SIZE;
		defaultimp.inputScale = 600;
		defaultimp.minBatchSize = 33;
		defaultimp.maxBatchSize = 65;
		// textures
		defaultimp.layerList.resize(3);
		defaultimp.layerList[0].worldSize = 100;
		defaultimp.layerList[0].textureNames.push_back("dirt_grayrocky_diffusespecular.dds");
		defaultimp.layerList[0].textureNames.push_back("dirt_grayrocky_normalheight.dds");
		defaultimp.layerList[1].worldSize = 30;
		defaultimp.layerList[1].textureNames.push_back("grass_green-01_diffusespecular.dds");
		defaultimp.layerList[1].textureNames.push_back("grass_green-01_normalheight.dds");
		defaultimp.layerList[2].worldSize = 200;
		defaultimp.layerList[2].textureNames.push_back("growth_weirdfungus-03_diffusespecular.dds");
		defaultimp.layerList[2].textureNames.push_back("growth_weirdfungus-03_normalheight.dds");
		*/
		
	}

	TerrainObject* TerrainManager::createFlatTerrain(int slotX,int slotY,int diffuseTexIndex)
	{
		TerrainObject* obj=TerrainObject::get(slotX,slotY);
		if(obj){//already exist.
			return obj;//now just return.@Todo make an exception system
		}
		_terrainGroup->defineTerrain(slotX, slotX, 0.0f);
		_terrainGroup->loadTerrain(slotX,slotY,true);
		_terrainGroup->freeTemporaryResources();

		
		Ogre::Terrain* terrain = _terrainGroup->getTerrain(slotX,slotY);
		

		_selectedTerrain=TerrainObject::addTerrain(_sceneObject, terrain,slotX,slotY);
		addTerrainLayer(_selectedTerrain,diffuseTexIndex);
		return _selectedTerrain;
	}

	void TerrainManager::initTerrainGlobalOptions()
	{
		//Ogre::Light* l=_sceneMgr->createLight("testLight");
		//Ogre::Vector3 lightdir(0.55, -0.3, 0.75);
		//lightdir.normalise();

		//l->setType(Ogre::Light::LT_DIRECTIONAL);
		//l->setDirection(lightdir);
		//l->setDiffuseColour(Ogre::ColourValue::White);
		//l->setSpecularColour(Ogre::ColourValue(0.4, 0.4, 0.4));

		//_sceneMgr->setAmbientLight(Ogre::ColourValue(0.2, 0.2, 0.2));

		Ogre::TerrainMaterialGeneratorPtr ptr(OGRE_NEW TerrainMaterialGeneratorWithDecal(_decalFrustum));

		_terrainGlobalOptions = OGRE_NEW Ogre::TerrainGlobalOptions();
		_terrainGlobalOptions->setMaxPixelError(8);
		_terrainGlobalOptions->setCompositeMapSize(1024);
		_terrainGlobalOptions->setCompositeMapDistance(3000);
		
		_terrainGlobalOptions->setDefaultMaterialGenerator(ptr);
		//_terrainGlobalOptions->setDefaultResourceGroup(GameEngine::TERRAIN_RESOURCE_GROUP_NAME);

		//_terrainGlobalOptions->setLightMapDirection(l->getDerivedDirection());
		_terrainGlobalOptions->setCompositeMapAmbient(_sceneMgr->getAmbientLight());
		//_terrainGlobalOptions->setCompositeMapDiffuse(l->getDiffuseColour());

		
	}

	TerrainObject* TerrainManager::getTerrain(int mouseX,int mouseY)
	{
		Ogre::Ray ray=_sceneObject->getRay(mouseX,mouseY);
		Ogre::TerrainGroup::RayResult rayResult = _terrainGroup->rayIntersects(ray);
		if (rayResult.hit){
			TerrainObject* obj= TerrainObject::get(rayResult.terrain);
			return obj;
		}
		return 0;
	}

	TerrainObject* TerrainManager::selectTerrain(int mouseX,int mouseY)
	{
		Ogre::Ray ray=_sceneObject->getRay(mouseX,mouseY);
		Ogre::TerrainGroup::RayResult rayResult = _terrainGroup->rayIntersects(ray);
		if (rayResult.hit){//hit
			_selectedTerrain= TerrainObject::get(rayResult.terrain);
			//when terrain is selected we default to edit terrain height.
			
			return _selectedTerrain;
		}
		//not selected.
		if(_selectedTerrain){
			_decalFrustum->setVisible(false);
			
		}

		_selectedTerrain=0;
		return 0;
	}

	TerrainObject* TerrainManager::selectTerrain(int id)
	{
		Object* object= Object::getObject(id);
		if(object->getObjectType()==OBJECT_TYPE_TERRAIN_OBJECT){
			TerrainObject* terrainObject=(TerrainObject*)object;
			Ogre::Terrain* terrain=terrainObject->getTerrain();
			_selectedTerrain= selectTerrain(terrain);
			return _selectedTerrain;
		}
		//shouldn't be
		_selectedTerrain=0;
		return 0;
	}

	TerrainObject* TerrainManager::selectTerrain(Ogre::Terrain* terrain)
	{
		if(terrain){
			_selectedTerrain=TerrainObject::get(terrain);
			return _selectedTerrain;
		}
		return 0;
	}

	void TerrainManager::update(float deltaT)
	{
		if(!_selectedTerrain){
			_decalFrustum->setVisible(false);
			return;
		}
		_terrainGroup->update();
	}


	void TerrainManager::OnMouseMove(int x,int y)
	{
		if(_selectedTerrain){
			
		}else{
			_decalFrustum->setVisible(false);
		}

		_mouseX=x;
		_mouseY=y;
	}

}
